/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.shape.impl;

import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import who.tmap.common.TMapException;
import who.tmap.toolkit.shape.PolygonShape;

/**
 * TMap project
 * WHO 2004-2005 
 * 
 * @author stl
 */
public class BasePolygonShapeHelper {

    // used by method generalPathToArray to count closed polygons
    protected int closedPolygonCt =0;
    
	/**
	 * Converts a general path into a list.
	 * The elements of the list returned are PolygonPoint
	 * Use the class attribute closedPolygonCt to count the closed polygons
	 * @see arrayToGeneralPath this method does the opposite operation
	 * @param path the general to convert
	 * @return a list containing the general path elements as PolygonPoint
	 */
	protected List<PolygonShapePoint> generalPathToArray( GeneralPath path ) {
	    float lastMoveToX =Float.MIN_VALUE, lastMoveToY =Float.MIN_VALUE;
	    List<PolygonShapePoint> arrPath =new ArrayList<PolygonShapePoint>();
		int type =-1;
		float[] c =new float[6];
		//int closedPolygonCt =0;
		
	    PathIterator it =path.getPathIterator(null);
	    while( !it.isDone() ) {
	        type =it.currentSegment(c);
	        
	        PolygonShapePoint info =new PolygonShapePoint();
	        
	        info.type =type;
	        info.originalType =type;
	        info.x =c[0];
	        info.y =c[1];
	        info.xDest =Float.MIN_VALUE;
	        info.yDest =Float.MIN_VALUE;
	        info.polygonId =closedPolygonCt;
	        
	        switch( type ) {
	        	case PathIterator.SEG_MOVETO:
	        	    lastMoveToX =c[0];
	        		lastMoveToY =c[1];
	        		info.polygonId =++closedPolygonCt;
	        		break;
	        		
	        	// To make things easier, we replace "close to" by "line to"
	        	case PathIterator.SEG_CLOSE:
	        	    info.type =PathIterator.SEG_LINETO;
        	    	info.x =lastMoveToX;
        	    	info.y =lastMoveToY;	        	
	        	    info.xDest =lastMoveToX;
	        		info.yDest =lastMoveToY;
	        		break;
	        }
	        
	        arrPath.add( info );
	        
	        it.next();
	    }	
	    
	    return arrPath;
	}  
	
	/**
	 * Convert a list of points into a general path
	 * @see generalPathToArray this method does the opposite operation
	 * @param polygonPts the list of points
	 * @return the general path
	 */
    @SuppressWarnings("unchecked")
	protected GeneralPath arrayToGeneralPath( List polygonPts ) {
        GeneralPath path =new GeneralPath();
	    float prevX =Float.MIN_VALUE, prevY =Float.MIN_VALUE;
	    float lastMoveToX =Float.MIN_VALUE, lastMoveToY =Float.MIN_VALUE;	    
        
        ListIterator list =polygonPts.listIterator();
        while( list.hasNext() ) {
            PolygonShapePoint psp =(PolygonShapePoint)list.next();
            
			switch (psp.type) {
				
					case PathIterator.SEG_MOVETO:
					    lastMoveToX =psp.x;
						lastMoveToY =psp.y;				    
					    path.moveTo( (float)psp.x, (float)psp.y );
					    break;
					    
					case PathIterator.SEG_LINETO:
				        if ( psp.x == lastMoveToX && psp.y == lastMoveToY ) {
				            path.closePath();	
				        } else {
				            if ( psp.x != prevX || psp.y != prevY ) {
				                path.lineTo( (float)psp.x, (float)psp.y );
				            }
				        }
						break;
						
					case PathIterator.SEG_CLOSE:
					    if ( path.getCurrentPoint() != null ) {
					        path.closePath();
					    }
					    break;
			} // switch	 	
			
			prevX =psp.x;
			prevY =psp.y;
		                
        } // while
        
	    if ( path.getCurrentPoint() != null ) {
	        path.closePath();
	    }
        
        return path;
    }	
	
	/**
	 * Extract list of points for the specified join id only 
	 * @param joinId the join id
	 * @param list the list of points
	 * @return a list of points for the specified join id
	 */
	protected List<PolygonShapePoint> extractPointsForPolygonIdOnly( int joinId, List<PolygonShapePoint> list ) {
	    List<PolygonShapePoint> newList =new ArrayList<PolygonShapePoint>();

	    for( PolygonShapePoint psp : list ) {
	        if ( psp.polygonId == joinId ) {
	            newList.add( psp );
	        }
	    }
	    
	    return newList;
	}
	
	/**
	 * Remove all the points for the specified polygon id
	 * @param polygonId the polygon id to remove
	 * @param list a list of points
	 */
	protected void removePointsForPolygonId( int polygonId, ArrayList<PolygonShapePoint> list ) {
	    List<PolygonShapePoint> newList =new ArrayList<PolygonShapePoint>();
	    for( PolygonShapePoint psp : list ) {	        
	        if ( psp.polygonId != polygonId ) {
	            newList.add( psp );
	        }
	    }
	    
	    list.retainAll( newList );
	}
	
	/**
	 * look for a Point2D in a list of points and returns the corresponding PolygonShapePoint
	 * @param points the list of points
	 * @param pt the point to look for
	 * @return the corresponding PolygonShapePoint
	 * @throws TMapException
	 */
    @SuppressWarnings("unchecked")
	protected PolygonShapePoint findPoint( List<PolygonShapePoint> points, Point2D pt ) {
        ListIterator lit =points.listIterator();        
        
        while( lit.hasNext() ) {
            PolygonShapePoint psp =(PolygonShapePoint)lit.next();  
            if ( psp.x == pt.getX() && psp.y == pt.getY() ) {
                return psp;
            }            
        }
        
        return null;
    }	
    
    protected CircularListIterator iterateUntilPoint( ArrayList<PolygonShapePoint> points, Point2D pt, int direction ) {
        CircularListIterator cli =new CircularListIterator( points );
        cli.setDirection( direction );
        while( !cli.hasTraversedAllElements() ) {
            PolygonShapePoint psp =(PolygonShapePoint)cli.iterate();
            
            if ( psp.x == pt.getX() && psp.y == pt.getY() ) {
                cli.resetNbSteps();
                return cli;
            }
        }
        
        return null;
    }
    
    @SuppressWarnings("unchecked")
	protected void addOtherPolygons( PolygonShape shape, int polygonId, List points ) {
        GeneralPath path =shape.getGeneralPath();
        ListIterator lit =points.listIterator(); 
        float prevX =Float.MIN_VALUE, prevY =Float.MIN_VALUE;
        int prevType =-1;
        int currentPolygonId =-1;
        
        while( lit.hasNext() ) {
            PolygonShapePoint psp =(PolygonShapePoint)lit.next();
            
            if ( psp.polygonId != polygonId ) {
				switch (psp.type) {
				
					case PathIterator.SEG_MOVETO:
		        	    prevX =psp.x;
	        			prevY =psp.y;
	        			prevType =psp.type;					    
					    path.moveTo( (float)psp.x, (float)psp.y );
					    break;
					    
					case PathIterator.SEG_LINETO: 
					    if ( path.getCurrentPoint() != null ) {
					        if ( psp.x == prevX && psp.y == prevY ) {
					            path.closePath();					            
					        } else if ( currentPolygonId != psp.polygonId ) {
				        	    prevX =psp.x;
			        			prevY =psp.y;
			        			prevType =psp.type;					    
							    path.moveTo( (float)psp.x, (float)psp.y );
					        } else {
					            path.lineTo( (float)psp.x, (float)psp.y );
					        }
					    } else {
					        path.moveTo( (float)psp.x, (float)psp.y );
					    }
						break;
						
					case PathIterator.SEG_CLOSE:
					    if ( path.getCurrentPoint() != null ) {
					        path.closePath();
					    }
					    break;
				}
            }
				
			currentPolygonId =psp.polygonId;
        }
    }
    
}
